﻿using Interpreter;
using Model;
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
using ME = Model.Environment;

namespace Zellularautomat
{
	/// <summary>
	/// Interaktionslogik für WindowMain.xaml
	/// </summary>
	public partial class WindowMain : Window
	{
		private Controller controller;
		private Dictionary<CellDefinition, Color> colorsForCells;
		private string[] rules;
		private double cellEdgeLength;

		private WindowTrajectory windowTrajectory;

		public WindowMain()
		{
			controller = new Controller();
			controller.StepCalculated += controller_StepCalculated;
			InitializeComponent();
		}

		#region Events

		private void windowMain_Initialized(object sender, EventArgs e)
		{
			menuCmBxCellType.Items.Add("-- Zelltypen --");
			menuCmBxCellType.SelectedIndex = 0;
		}

		private void windowMain_SizeChanged(object sender, SizeChangedEventArgs e)
		{
			ResizeField();
		}

		private void menuItmPreferences_Click(object sender, RoutedEventArgs e)
		{
			if (controller.Started)
			{
				if (ErrorHandler.ShowQuestion("Möchten Sie die Simulation stoppen?", "Simulation läuft") == MessageBoxResult.Yes)
				{
					controller.PauseSimulation();
				}
				else
				{
					return;
				}
			}
			WindowPreferences windowPreferences;
			if (ME.Preferences == null)
			{
				windowPreferences = new WindowPreferences(controller);
			}
			else
			{
				windowPreferences = new WindowPreferences(controller, new List<CellDefinition>(ME.CellDefinitions.Values),
					colorsForCells, ME.Preferences, rules);
			}
			windowPreferences.Owner = this;
			if (windowPreferences.ShowDialog() == true)
			{
				ME.Preferences = windowPreferences.Preferences;
				colorsForCells = windowPreferences.ColorsForCells;
				rules = windowPreferences.Rules;
				ME.CellDefinitions = new Dictionary<string, CellDefinition>();
				foreach (var cd in windowPreferences.CellDefinitions)
				{
					ME.CellDefinitions.Add(cd.Name, cd);
				}
				InitSimulation();
			}
			windowPreferences = null;
		}

		private void menuItmRandomConfig_Click(object sender, RoutedEventArgs e)
		{
			if (ME.Preferences == null)
			{
				ErrorHandler.ShowInformation("Bitte legen Sie erst Ihre Einstellungen fest."
					, "Keine Daten vorhanden");
			}
			else
			{
				InitFieldAllocation();
				PaintField();
			}
		}

		private void menuItmStart_Click(object sender, RoutedEventArgs e)
		{
			if (ME.Cells != null)
			{
				controller.StartSimulation();
			}
			else
			{
				ErrorHandler.ShowInformation("Die Simulation kann erst gestart werden, wenn die Einstellungen gemacht wurden.", "Simulation");
			}
		}

		private void menuItmStep_Click(object sender, RoutedEventArgs e)
		{
			if (ME.Cells != null)
			{
				controller.CalculateStep();
			}
			else
			{
				ErrorHandler.ShowInformation("Die Simulation kann erst gestart werden, wenn die Einstellungen gemacht wurden.", "Simulation");
			}
		}

		private void menuItmPause_Click(object sender, RoutedEventArgs e)
		{
			controller.PauseSimulation();
		}

		private void menuItmTrajectory_Click(object sender, RoutedEventArgs e)
		{
			if (ME.Cells != null)
			{
				windowTrajectory.Show();
			}
		}

		private void controller_StepCalculated(object sender, EventArgs e)
		{
			PaintField();
		}

		private void cell_PropertyChanged(object sender, EventArgs e)
		{
			DrawCell(((Cell)sender).Position.X, ((Cell)sender).Position.Y);
		}

		void rectangle_MouseDown(object sender, MouseButtonEventArgs e)
		{
			if (e.ChangedButton == MouseButton.Left)
			{
				ChangeCellAtLocation((System.Drawing.Point)((Rectangle)sender).Tag);
			}
		}

		void rectangle_MouseEnter(object sender, MouseEventArgs e)
		{
			if (e.LeftButton == MouseButtonState.Pressed)
			{
				ChangeCellAtLocation((System.Drawing.Point)((Rectangle)sender).Tag);
			}
		}

		void rectangle_MouseUp(object sender, MouseButtonEventArgs e)
		{
			if (e.ChangedButton == MouseButton.Right)
			{
				Rectangle r = (Rectangle)sender;

				//Translate absolute position
				Point locationFromWindow = r.TranslatePoint(new Point(0, 0), this);

				//Get corresponding cell
				System.Drawing.Point cellPosition = (System.Drawing.Point)r.Tag;
				ucAttributes.Cell = ME.Cells[cellPosition.X, cellPosition.Y];

				popupRectangles.HorizontalOffset = locationFromWindow.X;
				popupRectangles.VerticalOffset = locationFromWindow.Y;
				popupRectangles.IsOpen = true;
			}
		}

		#endregion

		#region Work

		private void ResizeField()
		{
			if (ME.Preferences != null)
			{
				InitEdgeLength();

				InitRectangles();
			}
		}

		private void InitSimulation()
		{
			// Combobox for changing cellTypes in field manually
			InitComboboxForCellTypeSelection();

			InitField();

			InitEdgeLength();

			InitRectangles();
		}

		private void InitComboboxForCellTypeSelection()
		{
			menuCmBxCellType.Items.Clear();
			foreach (var cellDefinition in ME.CellDefinitions.Values)
			{
				menuCmBxCellType.Items.Add(cellDefinition);
			}
			menuCmBxCellType.SelectedIndex = 0;
		}

		private void InitField()
		{
			if (ME.Preferences.InitialField != null)
			{
				if (ME.Preferences.InitialField.GetLength(0) == ME.Preferences.SizeWidth && ME.Preferences.InitialField.GetLength(1) == ME.Preferences.SizeHeight)
				{
					ME.Cells = ME.Preferences.InitialField;
					return;
				}
				else
				{
					ErrorHandler.ShowInformation("Die initiale Belegung aus den geladenen Einstellungen konnte nicht gewählt werden, weil die Abmessungen des Feldes verändert wurden."
						, "Fehler bei Feldinitialisierung");
				}
			}
			ME.Cells = new Cell[ME.Preferences.SizeWidth, ME.Preferences.SizeHeight];
			InitFieldAllocation();
		}

		private void InitFieldAllocation()
		{
			ME.Trajectory.Initialize();
			if (windowTrajectory == null)
			{
				windowTrajectory = new WindowTrajectory(colorsForCells);
			}
			if (windowTrajectory.IsInitialized)
			{
				windowTrajectory.Initialize(colorsForCells);
			}
			CreateRandomConfiguration();
		}

		private void CreateRandomConfiguration()
		{
			Random random = new Random();
			for (int x = 0; x < ME.Cells.GetLength(0); x++)
			{
				for (int y = 0; y < ME.Cells.GetLength(1); y++)
				{
					double rd = random.NextDouble();
					double sum = 0;
					int index = 0;
					foreach (var kvPair in ME.Preferences.CellDistribution)
					{
						sum += kvPair.Value;
						if (sum >= rd)
						{
							ME.Cells[x, y] = new Cell(kvPair.Key, new System.Drawing.Point(x, y));
							ME.Cells[x, y].PropertyChanged += cell_PropertyChanged;
							ME.Trajectory.AddToCurrentValue(kvPair.Key);
							break;
						}
						index++;
					}
				}
			}
			ME.Trajectory.PrepareNextStep();
		}

		private void InitEdgeLength()
		{
			//cells should be squares, so their edge length should be minimum
			double absHeight = stPanelMain.ActualHeight - mainMenu.ActualHeight;
			double absWidth = stPanelMain.ActualWidth;
			cellEdgeLength = Math.Min((absHeight / ME.Preferences.SizeHeight), (absWidth / ME.Preferences.SizeWidth));
		}

		private void InitRectangles()
		{
			ClearRectangles();
			for (int y = 0; y < ME.Cells.GetLength(1); y++)
			{
				RowDefinition rd = new RowDefinition();
				rd.Height = new GridLength(cellEdgeLength);
				gridField.RowDefinitions.Add(rd);
			}

			for (int x = 0; x < ME.Cells.GetLength(0); x++)
			{
				ColumnDefinition cd = new ColumnDefinition();
				cd.Width = new GridLength(cellEdgeLength);
				gridField.ColumnDefinitions.Add(cd);

				for (int y = 0; y < ME.Cells.GetLength(1); y++)
				{
					Rectangle r = new Rectangle()
					{
						Width = cellEdgeLength,
						Height = cellEdgeLength,
						Fill = new SolidColorBrush(colorsForCells[ME.Cells[x, y].CellDefinition]),
						Tag = new System.Drawing.Point(x, y)
					};
					r.MouseEnter += rectangle_MouseEnter;
					r.MouseDown += rectangle_MouseDown;
					r.MouseUp += rectangle_MouseUp;
					gridField.Children.Add(r);
					Grid.SetColumn(r, x);
					Grid.SetRow(r, y);
				}
			}
			gridField.Width = ME.Cells.GetLength(0) * cellEdgeLength;
			gridField.Height = ME.Cells.GetLength(1) * cellEdgeLength;
		}

		private void ClearRectangles()
		{
			gridField.ColumnDefinitions.Clear();
			gridField.RowDefinitions.Clear();
			gridField.Children.Clear();
		}

		private void ChangeCellAtLocation(System.Drawing.Point location)
		{
			if (ME.Preferences != null && colorsForCells.ContainsKey(((CellDefinition)menuCmBxCellType.SelectedItem)))
			{
				ChangeCell(ref ME.Cells[location.X, location.Y]);
			}
		}

		private void ChangeCell(ref Cell currentCell)
		{
			CellDefinition cellDef = (CellDefinition)menuCmBxCellType.SelectedItem;
			currentCell.GUISetCellDefinition(cellDef);
		}

		delegate void PaintFieldCallback();
		private void PaintField()
		{
			if (!this.Dispatcher.CheckAccess())
			{
				Dispatcher.Invoke(new PaintFieldCallback(PaintField));
			}
			else
			{
				if (ME.Cells == null)
				{
					return;
				}
				for (int x = 0; x < ME.Cells.GetLength(0); x++)
				{
					for (int y = 0; y < ME.Cells.GetLength(1); y++)
					{
						DrawCell(x, y);
					}
				}
			}
		}

		delegate void DrawCellCallback(int x, int y);
		private void DrawCell(int x, int y)
		{
			if (!this.Dispatcher.CheckAccess())
			{
				Dispatcher.Invoke(new DrawCellCallback(DrawCell), x, y);
			}
			else
			{
				((Rectangle)gridField.Children[GetGridIndex(x, y)]).Fill = new SolidColorBrush(colorsForCells[ME.Cells[x, y].CellDefinition]);
			}
		}

		private int GetGridIndex(int x, int y)
		{
			return ME.Cells.GetLength(1) * x + y;
		}

		#endregion

	}
}
